#include "code_generator.h"

CodeGenerator::CodeGenerator()
{
    operations_tree = new TreeOutput;
    asm_code_file.open("output.s");
    asm_code_file << "                            /* ----This file is generated compiler. Store any data in it is prohibited!---- */";
    asm_code_file << "\n.data\nprint_format:\n\t.string \"%d\\n\"\n";
    asm_code_file << "\n.text\n.globl main\n\n";
}

void CodeGenerator::__priorityToOperations(TreeOutput* tree, uint pos_in_parent_vector, bool allow_sub)
{
    std::vector<TreeOutput*>& child_nodes = tree->getChildNodes();
    if (child_nodes.size() == 1) return;
    ushort mul_or_div = Multiplication | Division; // Multiplication | Division == Division
    TokenType node_type = tree->getNodeType();
    TokenType ch_node_type_right = child_nodes[1]->getNodeType(); // тип правого дочірнього вузла
    TokenType ch_node_type_left = child_nodes[0]->getNodeType(); // тип лівого дочірнього вузла
    if (allow_sub && ((ch_node_type_right | mul_or_div) == mul_or_div)) return;
    bool allow_mul_or_div = ((node_type | mul_or_div) == mul_or_div) && ((ch_node_type_right | node_type) != Multiplication) && (ch_node_type_right & Operation);
    ushort child_node_pos = 1;
    if (allow_mul_or_div || allow_sub)
    {if (allow_sub) std::cout << "\t\t" << child_nodes[child_node_pos]->getDepth() - tree->getDepth() << '\n';
        if ((child_nodes[child_node_pos]->getDepth() - tree->getDepth()) <= 100) // потрібно поміняти вузли місцями
        {
            TreeOutput* child_node = child_nodes[child_node_pos];
            std::vector<TreeOutput*>& child_child_nodes = child_node->getChildNodes(); // дочірні вузли вузла child_node
            child_nodes[child_node_pos] = child_child_nodes[0]; // зберігаємо лівий вузол в дочірніх вузлах потчного вузла
            child_nodes[child_node_pos]->setParentNode(tree);
            child_child_nodes[0] = tree;
            child_node->setDepth(tree->getDepth());
            tree->getParentNode()->getChildNodes()[pos_in_parent_vector] = child_node;
            child_node->setParentNode(tree->getParentNode());
            tree->setParentNode(child_node);
            tree = child_node;
            child_node = child_child_nodes[0];
            if (allow_sub) __priorityToOperations(child_node, 0, true);
            else __priorityToOperations(child_node, 0); // вираз 20 * 5 / 2 - 4
        }
        return;
    }
    if ((node_type == Add) || (node_type == Appropriation) || (node_type == Sub)) // випадок з відніманням
    {
        if (ch_node_type_left == Sub) __priorityToOperations(child_nodes[0], 0, true);
        if (ch_node_type_right == Sub) __priorityToOperations(child_nodes[1], 1, true);
    }
}

void CodeGenerator::__convertOutputTreeToOperationsTree(TreeOutput* tree, TreeOutput* root_node, uint pos_in_parent_vector, uint depth_node)
{
    //static uint depth_node = 0; // глубина вузла. Кожен вузол має значення глубини розміщення відносно корня. Забезпечує пріоритет опер.
    if (!isupper(tree->getNodeName()[0])) return; // термінальний вузол не може мати дочірніх вузлів
    ++depth_node;
    uint local_depth_node = depth_node;
    sint operation_node_pos = -1; // позиція першого вузла-оператора в поточному вузлі
    std::vector<TreeOutput*>& child_nodes = tree->getChildNodes();
    for (int i = 0; i < child_nodes.size();) {
        __convertOutputTreeToOperationsTree(child_nodes[i], root_node, i, depth_node);
        if (child_nodes.size() == 1) // поточний вузол має лише один дочірній вузол - крок 3
        {
            if (tree->getParentNode() != 0)
            {
                child_nodes[0]->setParentNode(tree->getParentNode());
                if (child_nodes[0]->getDepth() == 0) child_nodes[0]->setDepth(local_depth_node);
                child_nodes[0]->setRootNode(root_node);
                tree->getParentNode()->getChildNodes()[pos_in_parent_vector] = child_nodes[0];
            }
            else
            {
                child_nodes[0]->setParentNode(0);
                operations_tree = child_nodes[0];
            }
            child_nodes[0] = 0;
            delete tree;
            return;
        }
        std::string ch_node_name = child_nodes[i]->getNodeGrammarName();
        TokenType child_node_type = child_nodes[i]->getNodeType();
        if (!(child_node_type & Operation) && ((!isalpha(ch_node_name[0]) && !isdigit(ch_node_name[0])) || child_node_type == Keyword))
        { // VT-вузол не несе семантичного навантаження - крок 4
            delete child_nodes[i];
            child_nodes.erase(tree->getChildNodes().begin() + i);
            i = 0;
            continue;
        }
        if (child_nodes[i]->getDepth() == 0) child_nodes[i]->setDepth(local_depth_node);
        if ((child_node_type & Operation) && (operation_node_pos == -1))
            operation_node_pos = i;
        ++i;
    }
    if (operation_node_pos != -1) // крок 5
    {
        child_nodes = tree->getChildNodes();
        TreeOutput* op_node = child_nodes[operation_node_pos]; // вузол до якого приєднуємо
        tree->setVarName(op_node->getNodeName());
        tree->setNodeType(op_node->getNodeType());
        tree->setDepth(op_node->getDepth());
        child_nodes.erase(child_nodes.begin() + operation_node_pos);
        child_nodes.insert(child_nodes.begin() + operation_node_pos, op_node->getChildNodes().begin(), op_node->getChildNodes().end());
        for (int i = 0; i < child_nodes.size(); i++) {
            child_nodes[i]->setParentNode(tree);
            child_nodes[i]->setRootNode(root_node);
        }
        op_node->getChildNodes().clear();
        delete op_node;
        __priorityToOperations(tree, pos_in_parent_vector); // якщо необхідно - перетворюємо дерево для виконання пріоритету операцій
    }
    else // VT-символи потрібно прикріпити до найближчого вузла-оператора
    {
        tree->getParentNode()->getChildNodes()[pos_in_parent_vector] = 0;
        tree->getParentNode()->getChildNodes().erase(tree->getParentNode()->getChildNodes().begin() + pos_in_parent_vector);
        std::vector<TreeOutput*>& child_nodes = tree->getChildNodes();
        std::vector<TreeOutput*>& parent_child_nodes = tree->getParentNode()->getChildNodes();
        parent_child_nodes.insert(parent_child_nodes.begin() + pos_in_parent_vector, child_nodes.begin(), child_nodes.end());
        for (int j = 0; j < tree->getChildNodes().size(); j++) {
            child_nodes[j]->setParentNode(tree->getParentNode());
            child_nodes[j]->setRootNode(root_node);
            child_nodes[j] = 0;
        }
        delete tree;
    }
}

void CodeGenerator::startCodeGenerate(const std::vector<TreeOutput*>& trees, std::map<std::string, std::map<std::string, IdentificatorEntry> >& i_t)
{
    func_identificators_table = i_t;
    __startCodeGenerate(trees);
}

void CodeGenerator::__startCodeGenerate(const std::vector<TreeOutput*>& trees)
{
    for (uint i = 0; i < trees.size(); i++) {
        TreeOutput* root_node = trees[i]->getChildNodes()[0];
        operations_tree->copyTreeOutput(trees[i]);
        __convertOutputTreeToOperationsTree(operations_tree, root_node);
        asm_code_file << operations_tree->getNodeName() << ":\n"; // описуємо ф-цію
        __placementLocalVariables(func_identificators_table[operations_tree->getNodeName()]);
        identificators_table = func_identificators_table[operations_tree->getNodeName()];
        for (uint j = 0; j < operations_tree->getChildNodes().size(); j++)
            __sysStartCodeGenerate(operations_tree->getChildNodes()[j]);
        asm_code_file << "\n\tmovl %ebp, %esp\n\tpopl %ebp\n\tret\n"; // "епілог";
        array_operations_trees.push_back(operations_tree);
        operations_tree = new TreeOutput;
    }
    operations_tree = 0;
}

void CodeGenerator::__sysStartCodeGenerate(TreeOutput* tree)
{
    if (tree->getNodeType() == Loop) __loopsCodeGenerator(tree);
    else if (tree->getNodeType() == Conditional) __conditionalCodeGenerator(tree);
    else if (tree->getNodeType() == Function) __callFunctionCodeGenerator(tree);
    else if (tree->getNodeType() & Operation) __variablesOperationsGenerate(tree);
}

void CodeGenerator::__placementLocalVariables(std::map<std::string, IdentificatorEntry> &ident_table)
{
    asm_code_file << "\tpushl %ebp\n\tmovl %esp, %ebp\n\n"; // "пролог"
    sint sum_size = 0, param_size = 0;
    std::map<std::string, IdentificatorEntry>::iterator it;
    for (it = ident_table.begin(); it != ident_table.end(); it++) {
        if (it->second.isFunctionParametr()) // параметри визваної ф-ції розміщуємо в стеку визиваючої ф-ції
        {
            // фіксуємо зміщення відносно регістра %ebp, щоб знати де знаходяться параметри
            // визваної ф-ції. 4 байти в стеку займає адреса повернення, тож 8(%ebp) - адрес першого параметру ф-ції
            it->second.setOffsetStack(param_size + 8);
            param_size += it->second.getTypeSize();
            continue;
        }
        if ((it->second.getTypeVar() & Array) == Array) sum_size += it->second.getArraySize() * it->second.getTypeSize();
        else sum_size += it->second.getTypeSize();
        it->second.setOffsetStack(-sum_size); // фіксуємо зміщення відносно регістра %ebp, щоб знати де знаходиться локальна змінна
    }
    asm_code_file << "\tsubl $" << sum_size << ", %esp\n\n"; // резервуємо місце під локальні змінні
}

void CodeGenerator::__placementFunctionParam(std::map<std::string, IdentificatorEntry>& ident_table)
{
    /* Параметри ф-ції завжди розміщуються під корнем дерева починаючи з нульового індексу getChildNodes().
       Параметри визваної ф-ції розміщуємо в стеку визиваючої ф-ції. Параметри ф-ції start() ігноруються.
       Порядок розміщення параметрів ф-ції в стеку є обратним їх розміщенню при визові ф-ції. */

    uint sum_size = 0;
    std::map<std::string, IdentificatorEntry>::iterator it = ident_table.begin();
    while (it->second.isFunctionParametr() && it != ident_table.end()) {
        sum_size += it->second.getTypeSize();
        ++it;
    }
    asm_code_file << "\tsubl $" << sum_size << ", %esp\n"; // резервуємо місце для параметрів визваної ф-ції
}

void CodeGenerator::__callFunctionTextGenerate(TreeOutput* tree, uint sum_size)
{
    asm_code_file << "\tcall " << tree->getNodeName() << "\n";
    asm_code_file << "\taddl $" << sum_size << ", %esp\n";
    asm_code_file << "\tpopl %edx\n\tpopl %ecx\n\tpopl %ebx\n"; // повертаємо значення збережеих регістрів
}

void CodeGenerator::__callFunctionCodeGenerator(TreeOutput* tree)
{
    asm_code_file << "\tpushl %ebx\n\tpushl %ecx\n\tpushl %edx\n"; // зберігаємо основні регістри
    __placementFunctionParam(func_identificators_table[tree->getNodeName()]);
    uint sum_size = 0;
    bool allow = true;
    std::vector<TreeOutput*>& child_nodes = tree->getChildNodes();
    for (ushort next_operand = 0; next_operand < child_nodes.size();) {
        TokenType child_type = child_nodes[next_operand]->getNodeType();
        if (child_type == Literal) // параметр ф-ції - літерал
        {
            asm_code_file << "\tmovl $" << child_nodes[next_operand]->getNodeName() << ", " << sum_size << "(%esp)\n";
            ++next_operand;
        }
        else if (child_type == Identificator)// параметр ф-ції - змінна
        {
            std::string operand = __getOperand(tree, next_operand);
            asm_code_file << "\tmovl " << operand << ", %eax\n\tmovl %eax, " << sum_size << "(%esp)\n";
        }
        else if ((child_type | 0b1100) != 0b1100) // після визову ф-ції розміщуються інші синтаксичні конструкції
        {
            if (allow) // генеруємо код для поточної ф-ції
            {
                __callFunctionTextGenerate(tree, sum_size);
                allow = false;
            }
            __sysStartCodeGenerate(child_nodes[next_operand]);
            ++next_operand;
            continue;
        }
        sum_size += 4;
        if ((next_operand == (child_nodes.size())) && allow) // ф-ція не містить після себе інших синтаксичних констукцій
        {
            __callFunctionTextGenerate(tree, sum_size);
            allow = false;
        }
    }
    if (allow) // ф-ція не містить параметрів
        __callFunctionTextGenerate(tree, sum_size);
}

void CodeGenerator::__conditionalCodeGenerator(TreeOutput* tree)
{
    static sint conditional_label_counter = 0;
    sint _conditional_label_counter = ++conditional_label_counter;
    __variablesOperationsGenerate(tree->getChildNodes()[0]); // генерація умови
    asm_code_file << "\ttestb %al, %al\n\tje .IF" << _conditional_label_counter << "\n\n"; // не дорівнює умові
    for (sint i = 1; i < tree->getChildNodes().size(); i++) {
        if (tree->getChildNodes()[i]->getNodeName() != "else")
        {
            __sysStartCodeGenerate(tree->getChildNodes()[i]); // генерація тіла умовного оператора
            continue;
        }
        if (tree->getChildNodes()[i]->getNodeName() == "else")
        {
            sint __tmp_conditional_label_counter = _conditional_label_counter;
            _conditional_label_counter = ++conditional_label_counter;
            asm_code_file << "\tjmp .IF" << _conditional_label_counter << "\n\n"; // завершення виконання вітки умовного оператора
            asm_code_file << ".IF" << __tmp_conditional_label_counter << ":\n";
            __sysStartCodeGenerate(tree->getChildNodes()[i]->getChildNodes()[0]); // генеруємо тіло оператора else
        }
    }
    asm_code_file << ".IF" << _conditional_label_counter << ":\n";
}

std::string CodeGenerator::__arrayElementAddressCodeGenerator(TreeOutput* tree, sint element_pos, bool is_twin_operand)
{
    std::ostringstream operand;
    uint array_size = identificators_table[tree->getChildNodes()[element_pos]->getNodeName()].getArraySize();
    ushort type_size = identificators_table[tree->getChildNodes()[element_pos]->getNodeName()].getTypeSize();
    sint offset_stack = identificators_table[tree->getChildNodes()[element_pos]->getNodeName()].getOffsetStack();
    if (tree->getChildNodes()[element_pos + 1]->getNodeType() == Identificator) // індексом масиву є змінна
    {
        sint array_index_address = identificators_table[tree->getChildNodes()[element_pos + 1]->getNodeName()].getOffsetStack();
        if (is_twin_operand) // вузол має два операнди, які є елементами масиву
        {
            asm_code_file << "\tmovl " << array_index_address << "(%ebp), %ebx\n";
            operand << offset_stack << "(%ebp, %ebx, " << type_size << ")";
        }
        else
        {
            asm_code_file << "\tmovl " << array_index_address << "(%ebp), %ecx\n";
            operand << offset_stack << "(%ebp, %ecx, " << type_size << ")";
        }
        return operand.str();
    }
    sint array_index = atoi(tree->getChildNodes()[element_pos + 1]->getNodeName().c_str()); // індексом масиву є ціле число
    try {
        if (array_index >= array_size) throw Exceptions();
    }
    catch (Exceptions) { // помилка: вихід за межі масиву. Індексація масиву починається з нуля
        std::cout << "Error: many elements for array\n";
        abort();
    }
    operand  << (offset_stack + array_index * type_size) << "(%ebp)";
    return operand.str();
}

ushort CodeGenerator::__initArrayCodeGenerator(TreeOutput* tree)
{
    sint array_size = atoi(tree->getChildNodes()[1]->getNodeName().c_str());
    try {
        if ((tree->getChildNodes().size() - 2) > array_size) throw Exceptions();
    }
    catch (Exceptions) { // помилка: вихід за межі масиву. Індексація масиву починається з нуля
        std::cout << "Error: many elements for array\n";
        abort();
    }
    sint i = 2, var_address = 0, array_element_address;
    for (; i < tree->getChildNodes().size(); i++) { // ініціалізація масиву
        array_element_address = identificators_table[tree->getChildNodes()[0]->getNodeName()].getOffsetStack() + var_address;
        asm_code_file << "\tmovl $" << tree->getChildNodes()[i]->getNodeName() << ", " << array_element_address << "(%ebp)\n";
        var_address += 4;
    }
    --i;
    while (i <= array_size) { // елементи масиву, які не були ініціалізовані - заповнюються нулями
        array_element_address = identificators_table[tree->getChildNodes()[0]->getNodeName()].getOffsetStack() - var_address;
        asm_code_file << "\tmovl $0, " << array_element_address << "(%ebp)\n";
        var_address += 4;
        ++i;
    }
}

std::string CodeGenerator::__getOperand(TreeOutput* tree, ushort& element_pos, bool is_twin_operand)
{
    std::ostringstream operand;
    if (identificators_table[tree->getChildNodes()[element_pos]->getNodeName()].getTypeVar() & Array) // операнд - елемент масива
    {
        operand << __arrayElementAddressCodeGenerator(tree, element_pos, is_twin_operand);
        element_pos += 2;
        return operand.str();
    }
    // операнд - проста змінна
    sint var_address = identificators_table[tree->getChildNodes()[element_pos++]->getNodeName()].getOffsetStack();
    operand  << var_address << "(%ebp)";
    return operand.str();
}

void CodeGenerator::__dualNodesAreOperands(TreeOutput* tree, std::ostringstream& operand1, std::ostringstream& operand2)
{
    ushort element_pos = 0;
    if (tree->getChildNodes()[0]->getNodeType() == Literal)
    {
        operand1 << "$" << tree->getChildNodes()[0]->getNodeName();
        ++element_pos;
    }
    else operand1 << __getOperand(tree, element_pos);
    if (tree->getChildNodes()[element_pos]->getNodeType() == Literal)
        operand2 << "$" << tree->getChildNodes()[element_pos]->getNodeName();
    else
        operand2 << __getOperand(tree, element_pos, true);
}

void CodeGenerator::__leftNodeIsOperation(TreeOutput* tree, std::ostringstream& operand)
{
    __variablesOperationsGenerate(tree->getChildNodes()[0]);
    if (tree->getChildNodes()[1]->getNodeType() == Literal) operand << "$" << tree->getChildNodes()[1]->getNodeName();
    else
    {
        ushort element_pos = 1;
        operand <<  __getOperand(tree, element_pos);
    }
}

void CodeGenerator::__rightNodeIsOperation(TreeOutput* tree, std::ostringstream& operand)
{
    ushort element_pos = 0;
    __getNextOperand(tree, element_pos);
    __variablesOperationsGenerate(tree->getChildNodes()[element_pos]);
    element_pos = 0;
    std::string tmp_operand =  __getOperand(tree, element_pos);
    if (tree->getChildNodes()[0]->getNodeType() == Literal) operand << "$" << tree->getChildNodes()[0]->getNodeName();
    else operand << tmp_operand;
}

void CodeGenerator::__appropriationOperationCodeGenerator(TreeOutput* tree)
{
    ushort next_operand = 0; // вказує на наступний операнд в операції (тому що існує індекс масива)
    std::string source_operand; // змінна, яка присвоюється змінній destination_var
    std::string destination_var; // змінна, якій присвоюється значення (source_operand)
    sint others_nodes; // містить номер наступного можливого вузла для подальшої генерації коду
    if (identificators_table[tree->getChildNodes()[0]->getNodeName()].getTypeVar() & Array) // присвоєння елементу масива
    {
        if ((tree->getChildNodes().size() >= 3) && (isdigit(tree->getChildNodes()[2]->getNodeName()[0]))) // ініціалізація масиву
        {
            __initArrayCodeGenerator(tree);
            return;
        }
        others_nodes = 4;
    }
    else // присвоєння простій змінній
        others_nodes = 3;
    destination_var = __getOperand(tree, next_operand);
    if (tree->getChildNodes()[next_operand]->getNodeType() == Function) // ф-ція повертає значення, яке присвоюється змінній
    {
        __sysStartCodeGenerate(tree->getChildNodes()[next_operand]);
        asm_code_file << "\tmovl %eax, " << destination_var << "\n";
    }
    else if (tree->getChildNodes()[next_operand]->getNodeType() & Operation)
    {
        asm_code_file << "\tpushl %ecx\n";
        __variablesOperationsGenerate(tree->getChildNodes()[next_operand]);
        asm_code_file << "\tpopl %ecx\n";
        asm_code_file << "\tmovl %eax, " << destination_var << "\n";
    }
    else
    {
        if (tree->getChildNodes()[next_operand]->getNodeType() == Literal)
            source_operand = "$" + tree->getChildNodes()[next_operand]->getNodeName();
        else
            source_operand = __getOperand(tree, next_operand, true);
        asm_code_file << "\tmovl " << source_operand << ", %eax\n\tmovl %eax, " << destination_var << "\n";
    }
    if (tree->getChildNodes().size() >= others_nodes) // перевіряємо інші гілки для вузла "="
    {
        for (sint i = others_nodes - 1; i < tree->getChildNodes().size(); i++)
            __sysStartCodeGenerate(tree->getChildNodes()[i]);
    }
}

void CodeGenerator::__moduleDivisionOperationCodeGenerator(TreeOutput* tree)
{
    ushort element__array_pos = 0; // містить наступну правильну позицію для вузла операнда (існує, тому що масиви мають індекс)
    __getNextOperand(tree, element__array_pos);
    TreeOutput *child_node = tree->getChildNodes()[element__array_pos];
    if ((tree->getChildNodes()[0]->getNodeType() & Operation) && (child_node->getNodeType() & Operation))
    {
        __variablesOperationsGenerate(tree->getChildNodes()[0]);
        asm_code_file << "\tpushl %eax\n";
        __variablesOperationsGenerate(child_node);
        asm_code_file << "\tmovl %eax, %ebx\n\tpopl %eax\n\txorl %edx, %edx\n\tdivl %ebx\n\tmovl %edx, %eax\n";
    }
    else if (tree->getChildNodes()[0]->getNodeType() & Operation) // тільки лівий дочірній вузол - операція
    {
        std::ostringstream operand;
        __leftNodeIsOperation(tree, operand);
        asm_code_file << "\tmovl " << operand.str() << ", %ebx\n\txorl %edx, %edx\n\tdivl %ebx\n\tmovl %edx, %eax\n";
    }
    else if (child_node->getNodeType() & Operation) // тільки правий дочірній вузол - операція
    {
        std::ostringstream operand;
        __rightNodeIsOperation(tree, operand);
        asm_code_file << "\tmovl %eax, %ebx\n\tmovl " << operand.str() << ", %eax\n\txorl %edx, %edx\n\tdivl %ebx\n";
        asm_code_file << "\tmovl %edx, %eax\n";
    }
    else // дочірні вузли - операнди
    {
        std::ostringstream operand1, operand2;
        __dualNodesAreOperands(tree, operand1, operand2);
        asm_code_file << "\tmovl " << operand1.str() << ", %eax\n\tmovl " << operand2.str();
        asm_code_file << ", %ebx\n\txorl %edx, %edx\n\tdivl %ebx\n\tmovl %edx, %eax\n";
    }
}

void CodeGenerator::__multiplicationOperationCodeGenerator(TreeOutput* tree)
{
    ushort element__array_pos = 0; // містить наступну правильну позицію для вузла операнда (існує, тому що масиви мають індекс)
    __getNextOperand(tree, element__array_pos);
    std::vector<TreeOutput*> &child_nodes = tree->getChildNodes();
    TreeOutput *child_node = child_nodes[element__array_pos];
    if ((child_nodes[0]->getNodeType() & Operation) && (child_node->getNodeType() & Operation))
    {
        __variablesOperationsGenerate(child_nodes[0]);
        asm_code_file << "\tpushl %eax\n";
        __variablesOperationsGenerate(child_node);
        asm_code_file << "\tmovl %eax, %edx\n\tpopl %eax\n\tmull %edx\n";
    }
    else if (child_nodes[0]->getNodeType() & Operation) // тільки лівий дочірній вузол - операція
    {
        std::ostringstream operand;
        __leftNodeIsOperation(tree, operand);
        asm_code_file << "\t" << "movl " << operand.str() << ", %edx\n\tmull %edx\n";
    }
    else if (child_node->getNodeType() & Operation) // тільки правий дочірній вузол - операція
    {
        std::ostringstream operand;
        __rightNodeIsOperation(tree, operand);
        asm_code_file << "\tmovl %eax, %edx\n\tmovl " << operand.str() << ", %eax\n\tmull %edx\n";
    }
    else // дочірні вузли - операнди
    {
        std::ostringstream operand1, operand2;
        __dualNodesAreOperands(tree, operand1, operand2);
        asm_code_file << "\tmovl " << operand1.str() << ", %eax\n\tmovl " << operand2.str() << ", %edx\n\tmull %edx\n";
    }
}

void CodeGenerator::__relatedOperationsCodeGenerator(TreeOutput* tree)
{
    ushort element__array_pos = 0; // містить наступну правильну позицію для вузла операнда (існує, тому що масиви мають індекс)
    __getNextOperand(tree, element__array_pos);
    std::vector<TreeOutput*>& child_nodes = tree->getChildNodes();
    TreeOutput* child_node = child_nodes[element__array_pos];
    std::string operation;
    if (tree->getNodeName() == ">") operation = "setg";
    else if (tree->getNodeName() == "<") operation = "setl";
    else if (tree->getNodeName() == "==") operation = "sete";
    else if (tree->getNodeName() == ">=") operation = "setge";
    else if (tree->getNodeName() == "<=") operation = "setle";
    else if (tree->getNodeName() == "!=") operation = "setne";
    if ((child_nodes[0]->getNodeType() & Operation) && (child_node->getNodeType() & Operation))
    {
        __variablesOperationsGenerate(child_nodes[0]);
        asm_code_file << "\tpushl %eax\n";
        __variablesOperationsGenerate(child_node);
        asm_code_file << "\tmovl %eax, %edx\n\tpopl %eax\n\tcmpl %edx, %eax\n\t" << operation << " %al\n";
    }
    else if (child_nodes[0]->getNodeType() & Operation) // тільки лівий дочірній вузол - операція
    {
        std::ostringstream operand;
        __leftNodeIsOperation(tree, operand);
        asm_code_file << "\tcmpl " << operand.str() << ", %eax\n\t" << operation << " %al\n";
    }
    else if (child_node->getNodeType() & Operation) // тільки правий дочірній вузол - операція
    {
        std::ostringstream operand;
        __rightNodeIsOperation(tree, operand);
        asm_code_file << "\tmovl %eax, %edx\n\tmovl " << operand.str() << ", %eax\n\tcmpl %edx, %eax\n\t" << operation << " %al\n";
    }
    else // дочірні вузли - операнди
    {
        std::ostringstream operand1, operand2;
        __dualNodesAreOperands(tree, operand1, operand2);
        asm_code_file << "\tmovl " << operand1.str() << ", %eax\n\tcmpl " << operand2.str() << ", %eax\n\t" << operation << " %al\n";
    }
}

void CodeGenerator::__addSubOperationsCodeGenerator(TreeOutput* tree)
{
    ushort element__array_pos = 0; // містить наступну правильну позицію для вузла операнда (існує, тому що масиви мають індекс)
    __getNextOperand(tree, element__array_pos);
    TreeOutput *child_node = tree->getChildNodes()[element__array_pos];
    std::string operation;
    if (tree->getNodeName() == "+") operation = "addl";
    else if (tree->getNodeName() == "-") operation = "subl";
    if ((tree->getChildNodes()[0]->getNodeType() & Operation) && (child_node->getNodeType() & Operation))
    { // дочірні вузли - операції
        __variablesOperationsGenerate(tree->getChildNodes()[0]);
        asm_code_file << "\tpushl %eax\n";
        __variablesOperationsGenerate(child_node);
        asm_code_file << "\tmovl %eax, %edx\n\tpopl %eax\n\t" << operation << " %edx, %eax\n";
    }
    else if (tree->getChildNodes()[0]->getNodeType() & Operation) // тільки лівий дочірній вузол - операція
    {
        std::ostringstream operand;
        __leftNodeIsOperation(tree, operand);
        asm_code_file << "\t" << operation << " " << operand.str() << ", %eax\n";
    }
    else if (child_node->getNodeType() & Operation) // тільки правий дочірній вузол - операція
    {
        std::ostringstream operand;
        __rightNodeIsOperation(tree, operand);
        asm_code_file << "\tmovl %eax, %edx\n\tmovl " << operand.str() << ", %eax\n\t" << operation <<  " %edx, %eax\n";
    }
    else // дочірні вузли - операнди
    {
        std::ostringstream operand1, operand2;
        __dualNodesAreOperands(tree, operand1, operand2);
        asm_code_file << "\tmovl " << operand1.str() << ", %eax\n\t" << operation << " " <<  operand2.str() << ", %eax\n";
    }
}

void CodeGenerator::__divisionOperationCodeGenerator(TreeOutput* tree)
{
    ushort element__array_pos = 0; // містить наступну правильну позицію для вузла операнда (існує, тому що масиви мають індекс)
    __getNextOperand(tree, element__array_pos);
    TreeOutput *child_node = tree->getChildNodes()[element__array_pos];
    if ((tree->getChildNodes()[0]->getNodeType() & Operation) && (child_node->getNodeType() & Operation))
    {
        __variablesOperationsGenerate(tree->getChildNodes()[0]);
        asm_code_file << "\tpushl %eax\n";
        __variablesOperationsGenerate(child_node);
        asm_code_file << "\tmovl %eax, %ebx\n\tpopl %eax\n\txorl %edx, %edx\n\tdivl %ebx\n";
    }
    else if (tree->getChildNodes()[0]->getNodeType() & Operation) // тільки лівий дочірній вузол - операція
    {
        std::ostringstream operand;
        __leftNodeIsOperation(tree, operand);
        asm_code_file << "\tmovl " << operand.str() << ", %ebx\n\txorl %edx, %edx\n\tdivl %ebx\n";
    }
    else if (child_node->getNodeType() & Operation) // тільки правий дочірній вузол - операція
    {
        std::ostringstream operand;
        __rightNodeIsOperation(tree, operand);
        asm_code_file << "\tmovl %eax, %ebx\n\tmovl " << operand.str() << ", %eax\n\txorl %edx, %edx\n\tdivl %ebx\n";
    }
    else // дочірні вузли - операнди
    {
        std::ostringstream operand1, operand2;
        __dualNodesAreOperands(tree, operand1, operand2);
        asm_code_file << "\tmovl " << operand1.str() << ", %eax\n\tmovl " << operand2.str();
        asm_code_file << ", %ebx\n\txorl %edx, %edx\n\tdivl %ebx\n";
    }
}

void CodeGenerator::__variablesOperationsGenerate(TreeOutput* tree)
{
    if (tree->getNodeType() == Appropriation) // присвоєння
    {
        __appropriationOperationCodeGenerator(tree);
        return;
    }
    if (tree->getNodeType() == Multiplication) // множення
    {
        __multiplicationOperationCodeGenerator(tree);
        return;
    }
    if (tree->getNodeType() == Division) // ділення
    {
        __divisionOperationCodeGenerator(tree);
        return;
    }
    if (tree->getNodeType() == ModuleDivision) // ділення по модулю
    {
        __moduleDivisionOperationCodeGenerator(tree);
        return;
    }
    if (tree->getNodeType() == Related) // операції відношення
    {
        __relatedOperationsCodeGenerator(tree);
        return;
    }
    if ((tree->getNodeType()) == Add || (tree->getNodeType() == Sub)) // операції віднімання та додавання
        __addSubOperationsCodeGenerator(tree);
}

void CodeGenerator::__loopsCodeGenerator(TreeOutput* tree)
{
    static sint loops_label_counter = 0; // лічильник міток циклів
    sint tmp_label_counter = loops_label_counter++;
    if (tree->getNodeName() == "while") // генерація коду для цикла while (цикл з передумовою)
    {
        asm_code_file << "\tjmp " << ".L" << tmp_label_counter << '\n'; // перехід до умови
        asm_code_file << ".L" << tmp_label_counter + 1 << ":\n"; // мітка на перехід до тіла циклу
        loops_label_counter++;
        for (sint i = 1; i < tree->getChildNodes().size(); i++) // генерація тіла циклу
            __sysStartCodeGenerate(tree->getChildNodes()[i]);
        asm_code_file << ".L" << tmp_label_counter << ":\n"; // умова
        __variablesOperationsGenerate(tree->getChildNodes()[0]); // генерація умови. Умова завжди міститься по нульвому індексу getChild()
        asm_code_file << "\ttestb %al, %al\n\tjne .L" << tmp_label_counter + 1 << "\n\n";
    }
}

ushort CodeGenerator::__getNextOperand(TreeOutput* tree, ushort &element_pos)
{
    if (identificators_table[tree->getChildNodes()[element_pos]->getNodeName()].getTypeVar() & Array) // операнд - елемент масива
        return element_pos += 2;
    return ++element_pos;
}
